EMail: ERIC3@AppleLink.Apple.com or ERICTHREE@AOL.com
Display Manager sample code
PlayVideo is a Metrowerks project which makes use of the RequestVideo sample code API.
PlayVideo & RequestVideo together demonstrate the usage of the Display Manager introduced with the PowerMacs and integrated into the system under System 7.5. With the RequestVideo sample code library, developers will be able to explore the Display Manager API by changing bit depth and screen resolution on multisync displays on built-in, NuBus, and PCI based video. Display Manager 1.0 is built into the Systems included with the first PowerMacs up through System 7.5. Display Manager 2.0 was first introduced in System 7.5.2 and is built into the system for all Macintoshes with the release of System 7.5.3.
It is a good idea to reset the screen(s) to the original setting before exit since the call to RVSetVideoAsScreenPrefs() may not do the right thing under Display Manager 1.0 with certain video drivers.
Included with this project are:
New Displays.h and Video.h header files. You will need to install the new universal Displays.h and Video.h header files to compile this sample code. These new universal headers are included with the Display Manager Development Kit. You will need to install these new universal Displays.h and Video.h header files only if they are newer than the ones inside your development environment.
A link library called "DisplayLib" for the Display Manager 2.0 features (InterfaceLib contains the Display Manager 1.0 entry points). The link library is simply a namespace binding library with no code implementation behind it. The Display Manager glue library is needed to provide runtime code to access the Display Manager 2.0 features.
Note: Be sure the DisplayLib link library is not in the CFM search path for runtime binding with your application. Failure to do so can result in CFM binding your application to the link library at application runtime with the result of all Display Manager 2.0 routines transformed into empty function calls. It is best to place the DisplayLib link library in the same folder as all your other link libraries within your development system.
A runtime library called "Display Library" which contains the PowerPC glue code to use the Display Manager 2.0 API (the InterfaceLib contains the Display Manager 1.0 glue code). The Display Library is built into system 7.5.3 and later systems. To make use if the Display Manger 2.0 features from PowerPC code while running System 7.1.2 through 7.5.2 you must have the "Display Library" file within the Code Fragment Manager search path - in the same directory as the application or in the extensions folder.
An alternative is to use the MPW MergeFragment command to place the runtime library inside the application. The following MPW command will place the "Display Library" in the data fork of an application (provided the Display Library file is in the same folder as the application):
MergeFragment 'Display Library' MyApplication
Note that this MergeFragment command will only work with standard PowerPC applications. There can be no other data in the data fork except PEF containers.
Application Sample Code. This sample code will build a FAT application - an application that contains 68K code in code resources in the resource fork and PowerPC code in a PEF container in the data fork. You must build the 68K side of the app first since it is included in the PowerPC project for building the PowerPC side of the app.
RVGetCurrentVideoSetting is a wrapper around the Display Manager call DMGetCurrentMode and will return the current video setting. The GDevice field of the VideoRequestRec request record must be filled in on input and the mode (depth), data (timing), and video switch fields are set on output. This filled in record can then be passed into the RVSetVideoRequest routine to have the screen reset to a known setting. If Display Manager 2.0 is not installed, this routine will make the appropriate driver calls and look in the appropriate places for the current display and depth configuration and fill the request record in accordingly. The availFlags field of the request record is set to indicate that the current mode is "safe" by clearing the kModeValidNotSafeBit.
RVRequestVideoSetting will iterate through all the active display GDevices looking for a display that best matches the requested settings passed in via the VideoRequestRecPtr. If the user specified a particular GDevice to search (by passing a GDevice in requestRec.screenDevice) then only that GDevice is searched for a best fit of the requested video settings. Two different methods of searching are used depending on the version of the Display Manager installed. For systems with Display Manager 1.0, a search is done using the Slot Manager via the GetRequestTheDM1Way routine. GetRequestTheDM1Way looks at all the slot resources associated with the specified GDevice to determine the best timing and bit depth fit for the users request. Under the Display Manager 2.0, the GetRequestTheDM2Way routine searches the specified GDevice using new Display Manger 2.0 calls to search for video modes of both slot and PCI based video devices.
RVSetVideoRequest is a wrapper around the Display Manger call DMSetDisplayMode which is used to set the desired timing (screen resolution) and bit depth. Given a request record that has been filled in with the RVRequestVideoSetting or RVGetCurrentVideoSetting calls, RVSetVideoRequest verifies that at least a display mode (timing) and depth mode (bit depth) are present in the request record and calls DMSetDisplayMode. If we get a kDMDriverNotDisplayMgrAwareErr from the Display Manager, we must use the Quickdraw SetDepth call to set the bit depth using the desired depth mode. In this error case (when devices are not supported by the Display Manager for example), we can not set the display mode, but can only set the bit depth on the desired display. Under the Display Manager 2.0, the VDSwitchInfoRec is used to set the desired timing and bit depth. As a fall back, the Display Manager will use the display and depth mode settings which are also passed into the DMSetDisplayMode call. Before making the DMSetDisplayMode call, we set the GDevice to monochrome if we want 1-bit mode or color for all other bit depths using the SetDeviceAttribute call. This monochrome/color logic can be changed if you want to set 8 bit monochrome for example. An error of -1 is returned from this call if the Display Manager is not installed, or if an error from SetDepth is generated.
IMPORTANT: WARNING WHEN CHANGING THE SCREEN RESOLUTION
IMPORTANT: IN A MULTI-MONITOR ENVIRONMENT
IMPORTANT: Monitor gravitation (repositioning) is not supported under
IMPORTANT: Display Manager 1.0, and there is not currently any code
IMPORTANT: written in this sample library to provide even minimal
IMPORTANT: gravitate functionality.
IMPORTANT: Currently, changing the video settings on multi-monitor
IMPORTANT: systems under the Display Manager 1.0 will result in only
RVConfirmVideoRequest is passed the new, possibly unsafe, display setting in the requestRec. This routine is called after you have called RVSetVideoRequest with the request record in order to verify the new setting functions correctly. If the kModeValidNotSafeBit of the availFlags is set, the new mode is a valid mode, but may not be safe with the current display hardware attached. In the case where the new mode is safe (the kModeValidNotSafeBit is not set), this routine will simply return with noErr as the return result. In the case where the kModeValidNotSafeBit is set, a dialog is brought up for the user to confirm the new setting. If the user does not confirm the new setting, an error (-1) will be returned to indicate that you should switch back to a known good display setting. There are three ways the user can indicate a non-confirm. The first is to hit the cancel button in the dialog. The second is to hit either the return or enter keys (cancel is the default button). The third is to let the dialog time out - there is an 8 second time-out for the dialog since the user can may not see anything on the screen. The only way to get a confirmation is to hit the okay button with a mouse click which will return noErr back to the caller. Return values other than noErr indicate a failure to confirm the new setting and should be handled by setting the display to a known good state.
OSErr RVSetVideoAsScreenPrefs (void);
RVSetVideoAsScreenPrefs is a wrapper around the newly made public Display Manger call DMUseScreenPrefs. This call is implemented under both Display Manager 1.0 and 2.0. Under Display Manager 1.0, it is a good idea to reset the screen(s) to the original setting before you exit your application since the call to DMUseScreenPrefs may not do the right thing with certain video drivers. An error of -1 is returned from this call if the Display Manager is not installed.
GetRequestTheDM1Way is passed the request record and the current GDevice to search. Using the GDevice, GetRequestTheDM1Way first gets all the necessary slot information (spSlot, spID, spDrvrHW, etc.) to begin the search of the device. A search of all valid timings and all valid bit depths associated for each timing is then made. For each valid timing/depth mode available, a call to the FindBestMatch routine is made and the request record is updated as appropriate. GetRequestTheDM1Way not called if Display Manager 2.0 is present since Display Manager 2.0 can search both Slot Manager and PCI based video devices, and returns much more detailed information. In this case, GetRequestTheDM2Way is called which uses a much different search algorithm.
GetRequestTheDM2Way is used if the Display Manager 2.0 is installed. This routine uses the slot independent Display Manager API to search through available video drivers (including PCI based drivers), and thus is used over GetRequestTheDM1Way if possible. This routine is set up by the API call RVRequestVideoSetting which initializes the DMDisplayModeListIteratorUPP, DMListIndexType and DMListType values for this call. GetRequestTheDM2Way makes use the new Display Manager 2.0 call DMGetIndexedDisplayModeFromList to iterate through the <theDisplayModeList> for a count of <theDisplayModeCount> available timings supported by the specified GDevice. Before returning from the Display Manager routine DMGetIndexedDisplayModeFromList, a call back routine ( ModeListIterator() ) is called which copies the Display Manager data into our own data record. The specific data we look at is the bit depth, depth mode, timing resolution (horizontal and vertical), the timing mode, and the VDSwitchInfoRec. The call back routine then returns to the Display Manager which then returns back to GetRequestTheDM2Way. Once we have all the bit depths for a given timing mode, GetRequestTheDM2Way then loops through all the depths searching for the best fit to the users video request. This search continues until there are no more timing modes available for the GDevice, at which point it returns to RVRequestVideoSetting with a possible better fit to the users request.
pascal void ModeListIterator( void *userData,
DMListIndexType itemIndex,
DMDisplayModeListEntryPtr displaymodeInfo);
ModeListIterator is the sister routine of GetRequestTheDM2Way. This routine is a call back routine called by the Display Manager during a call to DMGetIndexedDisplayModeFromList, and is called with all the associated information about a particular timing mode for a display. The information copied to the <userData> record is the bit depth, depth mode, timing resolution (horizontal and vertical), the timing mode, and the VDSwitchInfoRec.
ConfirmAlertFilter is used when a call to RVConfirmVideoRequest is made and the user needs to confirm the new setting works correctly on the installed display hardware. This filter procedure will force a dialog time out after 8 seconds if the user does not respond. The cancel button is the default and is activated by the enter or return keys. To confirm the new setting, the user is required to hit the okay button with the mouse. A power user feature exists which allows the user to confirm with the enter or return keys while the option key is pressed.
FindBestMatch is used to find the best fit of the requested video settings to the available video settings. Given a request record, FindBestMatch uses the <bitDepth>, <horizontal>, and <vertical> values to determine if they are better or equal to the current match. Input values are set prior to calling RVRequestVideoSetting. Output values are set within the call to RVRequestVideoSetting. The video request record looks like the following:
struct VideoRequestRec {
GDHandle screenDevice;
short reqBitDepth;
short availBitDepth;
unsigned long reqHorizontal;
unsigned long reqVertical;
unsigned long availHorizontal;
unsigned long availVertical;
unsigned long requestFlags;
unsigned long availFlags;
unsigned long displayMode;
unsigned long depthMode;
VDSwitchInfoRec switchInfo;
};
where the fields are defined as follows:
screenDevice: Is an input/output field.
Input definition: The screenDevice field is used to specify two types of searches. If the screenDevice is set to an active GDHandle, then only that device will be looked at to match the requested video setting. For example, if you pass in the value returned from GetMainDevice, the only the main screen will be looked at for a possible match. If the screenDevice is set to <nil>, then all active GDevices will be looked at to make the best match of the requested video setting.
Output definition: the screenDevice field is set to the GDevice which is capable of best matching the requested video setting.
reqBitDepth: Is an input field.
Input definition: The reqBitDepth field is the requested bit depth for the new video setting. Appropriate values are 1, 2, 4, 8, 16, 32.
availBitDepth: Is an output field.
Output definition: The availBitDepth field is the bit depth that most closely matches the requested bit depth.
reqHorizontal: Is an input field.
Input definition: The reqHorizontal field is the requested horizontal resolution for the new video setting. To set to a 14" monitor, for example, a value of 640 would be appropriate.
reqVertical: Is an input field.
Input definition: The reqVertical field is the requested vertical resolution for the new video setting. To set to a 14" monitor, for example, a value of 480 would be appropriate.
availHorizontal: Is an output field.
Output definition: The availHorizontal field is the horizontal resolution that most closely matches the requested resolution.
availVertical: Is an output field.
Output definition: The availVertical field is the vertical resolution that most closely matches the requested resolution.
requestFlags: Is an input field.
Input definition: The requestFlags is a bit field that helps to shape the best fit search of the new video setting. The requestFlags are defined as follows:
enum {
kBitDepthPriorityBit = 0,
kAbsoluteRequestBit = 1,
kShallowDepthBit = 2,
kMaximizeResBit = 3
};
kBitDepthPriorityBit: Should be set to force a best fit depth resolution over a best fit screen resolution since there may be cases where the best screen resolution match does not allow for the specified bit depth.
kAbsoluteRequestBit: Should be set to force an exact match of the requested video setting.
kShallowDepthBit: Should be set to match the bet depth less than or equal to the requested bit depth since there may be cases where the available best fit bit depth is greater than the requested depth.
kMaximizeResBit: Should be set to match the screen resolution equal to or greater than the requested resolution since there may be cases where the available best fit screen resolution is less than the requested resolution.
When the requestFlags is set to zero, the default search algorithm of the FindBestMatch routine is used. This search is a combination of minimizing horizontal and vertical deltas off of the requested values while at the same time finding an equal or greater matching bit depth at. When any combination of requestFlags bits are set, the search behaves in an appropriate fashion. For example, to search with bit depth priority and maximize screen resolution, requestFlags would be set to (1<<kBitDepthPriorityBit) + (1<<kMaximizeResBit).
availFlags: Is an output field.
Output definition: The availFlags is a bit field that gives status on the available display mode. The availFlags are defined as follows:
enum {
kModeValidNotSafeBit = 0
};
kModeValidNotSafeBit: Is set if the available display mode is valid but not safe. A safe display mode is guaranteed to function properly, while a valid but unsafe mode is supported by the video hardware but may not display correctly on the given display hardware. If the available timing mode is valid but not safe, a user confirmation of the new setting will be required to verify that the new display mode works correctly.
When the availFlags field is set to zero, the available display mode is both valid and safe. Switching to this available display mode will result in a correctly set display. If the kModeValidNotSafeBit is set, the available display mode is supported by the video hardware, but may not be supported by the display hardware.
displayMode: Is an output field.
Output definition: The displayMode is the value used by the system to produce the horizontal/vertical resolution set in the availHorizontal/availVertical fields.
depthMode: Is an output field.
Output definition: The depthMode is the value used by the system to produce the bit depth set in the availBitDepth field.
switchInfo: Is an output field.
Output definition: The switchInfo is a record used by the Display Manager 2.0 as an alternate way to set the displayMode/depthMode of a GDevice.
void GravitateMonitors (void);
GravitateMonitors is not currently implemented. This code is to be called after the Display Manager call DMSetDisplayMode only if Display Manager 1.0 is running - Display Manager 2.0 does this work for us. The problem to be solved occurs only in multimonitor situations where it is possible to create a disjoint desktop region. For example, prior to the call to DMSetDisplayMode, two monitors are sitting side by side. We then set the leftmost monitor to a smaller screen resolution which forces the bottom right side of the monitor to be moved up and to the left. This results is a disjoint desktop since the left side of the rightmost monitor is no longer "attached" to the right side of the leftmost monitor. This problem can be exaggerated by the use of other monitors positioned in odd relative locations to each other.